/******************************************************************************
 * $Id: shpopen.c,v 1.39 2002/08/26 06:46:56 warmerda Exp $
 *
 * Project:  Shapelib
 * Purpose:  Implementation of core Shapefile read/write functions.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 ******************************************************************************
 * Copyright (c) 1999, 2001, Frank Warmerdam
 *
 * This software is available under the following "MIT Style" license,
 * or at the option of the licensee under the LGPL (see LICENSE.LGPL).  This
 * option is discussed in more detail in shapelib.html.
 *
 * --
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ******************************************************************************
 *
 */
/*
static char rcsid[] = 
"$Id: shpopen.c,v 1.39 2002/08/26 06:46:56 warmerda Exp $";
*/
#include "shapefil.h"

#include <math.h>
#include <limits.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char uchar;

#if UINT_MAX == 65535
typedef long int32;
#else
typedef int int32;
#endif

#ifndef FALSE
#  define FALSE		0
#  define TRUE		1
#endif

#define ByteCopy( a, b, c )	memcpy( b, a, c )
#ifndef MAX
#  define MIN(a,b)      ((a<b) ? a : b)
#  define MAX(a,b)      ((a>b) ? a : b)
#endif

static int 	bBigEndian;


/************************************************************************/
/*                              SwapWord()                              */
/*                                                                      */
/*      Swap a 2, 4 or 8 byte word.                                     */
/************************************************************************/

static void	SwapWord( int length, void * wordP )
{
	int	i;
	uchar temp;

	for( i=0; i < length/2; i++ )
	{
		temp = ((uchar *) wordP)[i];
		((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1];
		((uchar *) wordP)[length-i-1] = temp;
	}
}

/************************************************************************/
/*                             SfRealloc()                              */
/*                                                                      */
/*      A realloc cover function that will access a NULL pointer as     */
/*      a valid input.                                                  */
/************************************************************************/

static void * SfRealloc( void * pMem, int nNewSize )
{
	if( pMem == NULL )
		return( (void *) malloc(nNewSize) );
	else
		return( (void *) realloc(pMem,nNewSize) );
}

/************************************************************************/
/*                          SHPWriteHeader()                            */
/*                                                                      */
/*      Write out a header for the .shp and .shx files as well as the	*/
/*	contents of the index (.shx) file.				*/
/************************************************************************/

static void SHPWriteHeader( SHPHandle psSHP )
{
	uchar abyHeader[100];
	int i;
	int32 i32;
	double dValue;
	int32 *panSHX;

	/* -------------------------------------------------------------------- */
	/*      Prepare header block for .shp file.                             */
	/* -------------------------------------------------------------------- */
	for( i = 0; i < 100; i++ )
		abyHeader[i] = 0;

	abyHeader[2] = 0x27;				/* magic cookie */
	abyHeader[3] = 0x0a;

	i32 = psSHP->nFileSize/2;				/* file size */
	ByteCopy( &i32, abyHeader+24, 4 );
	if( !bBigEndian ) SwapWord( 4, abyHeader+24 );

	i32 = 1000;						/* version */
	ByteCopy( &i32, abyHeader+28, 4 );
	if( bBigEndian ) SwapWord( 4, abyHeader+28 );

	i32 = psSHP->nShapeType;				/* shape type */
	ByteCopy( &i32, abyHeader+32, 4 );
	if( bBigEndian ) SwapWord( 4, abyHeader+32 );

	dValue = psSHP->adBoundsMin[0];			/* set bounds */
	ByteCopy( &dValue, abyHeader+36, 8 );
	if( bBigEndian ) SwapWord( 8, abyHeader+36 );

	dValue = psSHP->adBoundsMin[1];
	ByteCopy( &dValue, abyHeader+44, 8 );
	if( bBigEndian ) SwapWord( 8, abyHeader+44 );

	dValue = psSHP->adBoundsMax[0];
	ByteCopy( &dValue, abyHeader+52, 8 );
	if( bBigEndian ) SwapWord( 8, abyHeader+52 );

	dValue = psSHP->adBoundsMax[1];
	ByteCopy( &dValue, abyHeader+60, 8 );
	if( bBigEndian ) SwapWord( 8, abyHeader+60 );

	dValue = psSHP->adBoundsMin[2];			/* z */
	ByteCopy( &dValue, abyHeader+68, 8 );
	if( bBigEndian ) SwapWord( 8, abyHeader+68 );

	dValue = psSHP->adBoundsMax[2];
	ByteCopy( &dValue, abyHeader+76, 8 );
	if( bBigEndian ) SwapWord( 8, abyHeader+76 );

	dValue = psSHP->adBoundsMin[3];			/* m */
	ByteCopy( &dValue, abyHeader+84, 8 );
	if( bBigEndian ) SwapWord( 8, abyHeader+84 );

	dValue = psSHP->adBoundsMax[3];
	ByteCopy( &dValue, abyHeader+92, 8 );
	if( bBigEndian ) SwapWord( 8, abyHeader+92 );

	/* -------------------------------------------------------------------- */
	/*      Write .shp file header.                                         */
	/* -------------------------------------------------------------------- */
	fseek( psSHP->fpSHP, 0, 0 );
	fwrite( abyHeader, 100, 1, psSHP->fpSHP );

	/* -------------------------------------------------------------------- */
	/*      Prepare, and write .shx file header.                            */
	/* -------------------------------------------------------------------- */
	i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2;   /* file size */
	ByteCopy( &i32, abyHeader+24, 4 );
	if( !bBigEndian ) SwapWord( 4, abyHeader+24 );

	fseek( psSHP->fpSHX, 0, 0 );
	fwrite( abyHeader, 100, 1, psSHP->fpSHX );

	/* -------------------------------------------------------------------- */
	/*      Write out the .shx contents.                                    */
	/* -------------------------------------------------------------------- */
	panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords);

	for( i = 0; i < psSHP->nRecords; i++ )
	{
		panSHX[i*2  ] = psSHP->panRecOffset[i]/2;
		panSHX[i*2+1] = psSHP->panRecSize[i]/2;
		if( !bBigEndian ) SwapWord( 4, panSHX+i*2 );
		if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 );
	}

	fwrite( panSHX, sizeof(int32) * 2, psSHP->nRecords, psSHP->fpSHX );

	free( panSHX );
}

/************************************************************************/
/*                              SHPOpen()                               */
/*                                                                      */
/*      Open the .shp and .shx files based on the basename of the       */
/*      files or either file name.                                      */
/************************************************************************/

SHPHandle SHPOpen( const char * pszLayer, const char * pszAccess )
{
	char *pszFullname, *pszBasename;
	SHPHandle psSHP;

	uchar *pabyBuf;
	int i;
	double dValue;

	/* -------------------------------------------------------------------- */
	/*      Ensure the access string is one of the legal ones.  We          */
	/*      ensure the result string indicates binary to avoid common       */
	/*      problems on Windows.                                            */
	/* -------------------------------------------------------------------- */
	if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 || strcmp(pszAccess,"r+") == 0 )
		pszAccess = "r+b";
	else
		pszAccess = "rb";

	/* -------------------------------------------------------------------- */
	/*	Establish the byte order on this machine.			*/
	/* -------------------------------------------------------------------- */
	i = 1;
	if( *((uchar *) &i) == 1 )
		bBigEndian = FALSE;
	else
		bBigEndian = TRUE;

	/* -------------------------------------------------------------------- */
	/*	Initialize the info structure.					*/
	/* -------------------------------------------------------------------- */
	psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1);

	psSHP->bUpdated = FALSE;

	/* -------------------------------------------------------------------- */
	/*	Compute the base (layer) name.  If there is any extension	*/
	/*	on the passed in filename we will strip it off.			*/
	/* -------------------------------------------------------------------- */
	pszBasename = (char *) malloc(strlen(pszLayer)+5);
	strcpy( pszBasename, pszLayer );
	for( i = strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'	&& pszBasename[i] != '\\'; i-- ) {}

	if( pszBasename[i] == '.' )
		pszBasename[i] = '\0';

	/* -------------------------------------------------------------------- */
	/*	Open the .shp and .shx files.  Note that files pulled from	*/
	/*	a PC to Unix with upper case filenames won't work!		*/
	/* -------------------------------------------------------------------- */
	pszFullname = (char *) malloc(strlen(pszBasename) + 5);
	sprintf( pszFullname, "%s.shp", pszBasename );
	psSHP->fpSHP = fopen(pszFullname, pszAccess );
	if( psSHP->fpSHP == NULL )
	{
		sprintf( pszFullname, "%s.SHP", pszBasename );
		psSHP->fpSHP = fopen(pszFullname, pszAccess );
	}

	if( psSHP->fpSHP == NULL )
	{
		free( psSHP );
		free( pszBasename );
		free( pszFullname );
		return( NULL );
	}

	sprintf( pszFullname, "%s.shx", pszBasename );
	psSHP->fpSHX = fopen(pszFullname, pszAccess );
	if( psSHP->fpSHX == NULL )
	{
		sprintf( pszFullname, "%s.SHX", pszBasename );
		psSHP->fpSHX = fopen(pszFullname, pszAccess );
	}

	if( psSHP->fpSHX == NULL )
	{
		fclose( psSHP->fpSHP );
		free( psSHP );
		free( pszBasename );
		free( pszFullname );
		return( NULL );
	}

	free( pszFullname );
	free( pszBasename );

	/* -------------------------------------------------------------------- */
	/*  Read the file size from the SHP file.				*/
	/* -------------------------------------------------------------------- */
	pabyBuf = (uchar *) malloc(100);
	fread( pabyBuf, 100, 1, psSHP->fpSHP );

	psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256
			+ pabyBuf[25] * 256 * 256
			+ pabyBuf[26] * 256
			+ pabyBuf[27]) * 2;

	/* -------------------------------------------------------------------- */
	/*  Read SHX file Header info                                           */
	/* -------------------------------------------------------------------- */
	fread( pabyBuf, 100, 1, psSHP->fpSHX );

	if( pabyBuf[0] != 0 || pabyBuf[1] != 0 || pabyBuf[2] != 0x27 || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) )
	{
		fclose( psSHP->fpSHP );
		fclose( psSHP->fpSHX );
		free( psSHP );

		return( NULL );
	}

	psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256 + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256;
	psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8;

	psSHP->nShapeType = pabyBuf[32];

	if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 )
	{
		/* this header appears to be corrupt.  Give up. */
		fclose( psSHP->fpSHP );
		fclose( psSHP->fpSHX );
		free( psSHP );

		return( NULL );
	}

	/* -------------------------------------------------------------------- */
	/*      Read the bounds.                                                */
	/* -------------------------------------------------------------------- */
	if( bBigEndian ) SwapWord( 8, pabyBuf+36 );
	memcpy( &dValue, pabyBuf+36, 8 );
	psSHP->adBoundsMin[0] = dValue;

	if( bBigEndian ) SwapWord( 8, pabyBuf+44 );
	memcpy( &dValue, pabyBuf+44, 8 );
	psSHP->adBoundsMin[1] = dValue;

	if( bBigEndian ) SwapWord( 8, pabyBuf+52 );
	memcpy( &dValue, pabyBuf+52, 8 );
	psSHP->adBoundsMax[0] = dValue;

	if( bBigEndian ) SwapWord( 8, pabyBuf+60 );
	memcpy( &dValue, pabyBuf+60, 8 );
	psSHP->adBoundsMax[1] = dValue;

	if( bBigEndian ) SwapWord( 8, pabyBuf+68 );		/* z */
	memcpy( &dValue, pabyBuf+68, 8 );
	psSHP->adBoundsMin[2] = dValue;

	if( bBigEndian ) SwapWord( 8, pabyBuf+76 );
	memcpy( &dValue, pabyBuf+76, 8 );
	psSHP->adBoundsMax[2] = dValue;

	if( bBigEndian ) SwapWord( 8, pabyBuf+84 );		/* z */
	memcpy( &dValue, pabyBuf+84, 8 );
	psSHP->adBoundsMin[3] = dValue;

	if( bBigEndian ) SwapWord( 8, pabyBuf+92 );
	memcpy( &dValue, pabyBuf+92, 8 );
	psSHP->adBoundsMax[3] = dValue;

	free( pabyBuf );

	/* -------------------------------------------------------------------- */
	/*	Read the .shx file to get the offsets to each record in 	*/
	/*	the .shp file.							*/
	/* -------------------------------------------------------------------- */
	psSHP->nMaxRecords = psSHP->nRecords;

	psSHP->panRecOffset = (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );
	psSHP->panRecSize = (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) );

	pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) );
	fread( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX );

	for( i = 0; i < psSHP->nRecords; i++ )
	{
		int32 nOffset, nLength;

		memcpy( &nOffset, pabyBuf + i * 8, 4 );
		if( !bBigEndian ) SwapWord( 4, &nOffset );

		memcpy( &nLength, pabyBuf + i * 8 + 4, 4 );
		if( !bBigEndian ) SwapWord( 4, &nLength );

		psSHP->panRecOffset[i] = nOffset*2;
		psSHP->panRecSize[i] = nLength*2;
	}
	free( pabyBuf );

	return( psSHP );
}

/************************************************************************/
/*                              SHPClose()                              */
/*								       	*/
/*	Close the .shp and .shx files.					*/
/************************************************************************/

void SHPClose(SHPHandle psSHP )
{
	/* -------------------------------------------------------------------- */
	/*	Update the header if we have modified anything.			*/
	/* -------------------------------------------------------------------- */
	if( psSHP->bUpdated )
	{
		SHPWriteHeader( psSHP );
	}

	/* -------------------------------------------------------------------- */
	/*      Free all resources, and close files.                            */
	/* -------------------------------------------------------------------- */
	free( psSHP->panRecOffset );
	free( psSHP->panRecSize );

	fclose( psSHP->fpSHX );
	fclose( psSHP->fpSHP );

	if( psSHP->pabyRec != NULL )
	{
		free( psSHP->pabyRec );
	}

	free( psSHP );
}

/************************************************************************/
/*                             SHPGetInfo()                             */
/*                                                                      */
/*      Fetch general information about the shape file.                 */
/************************************************************************/

void SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, double * padfMinBound, double * padfMaxBound )
{
	int i;
	if( pnEntities != NULL )
		*pnEntities = psSHP->nRecords;

	if( pnShapeType != NULL )
		*pnShapeType = psSHP->nShapeType;

	for( i = 0; i < 4; i++ )
	{
		if( padfMinBound != NULL )
			padfMinBound[i] = psSHP->adBoundsMin[i];
		if( padfMaxBound != NULL )
			padfMaxBound[i] = psSHP->adBoundsMax[i];
	}
}

/************************************************************************/
/*                             SHPCreate()                              */
/*                                                                      */
/*      Create a new shape file and return a handle to the open         */
/*      shape file with read/write access.                              */
/************************************************************************/

SHPHandle SHPCreate( const char * pszLayer, int nShapeType )
{
	char *pszBasename, *pszFullname;
	int i;
	FILE *fpSHP, *fpSHX;
	uchar abyHeader[100];
	int32 i32;
	double dValue;

	/* -------------------------------------------------------------------- */
	/*      Establish the byte order on this system.                        */
	/* -------------------------------------------------------------------- */
	i = 1;
	if( *((uchar *) &i) == 1 )
		bBigEndian = FALSE;
	else
		bBigEndian = TRUE;

	/* -------------------------------------------------------------------- */
	/*	Compute the base (layer) name.  If there is any extension	*/
	/*	on the passed in filename we will strip it off.			*/
	/* -------------------------------------------------------------------- */
	pszBasename = (char *) malloc(strlen(pszLayer)+5);
	strcpy( pszBasename, pszLayer );
	for( i = strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'	&& pszBasename[i] != '\\'; i-- ) {}

	if( pszBasename[i] == '.' )
		pszBasename[i] = '\0';

	/* -------------------------------------------------------------------- */
	/*      Open the two files so we can write their headers.               */
	/* -------------------------------------------------------------------- */
	pszFullname = (char *) malloc(strlen(pszBasename) + 5);
	sprintf( pszFullname, "%s.shp", pszBasename );
	fpSHP = fopen(pszFullname, "wb" );
	if( fpSHP == NULL )
		return( NULL );

	sprintf( pszFullname, "%s.shx", pszBasename );
	fpSHX = fopen(pszFullname, "wb" );
	if( fpSHX == NULL )
		return( NULL );

	free( pszFullname );
	free( pszBasename );

	/* -------------------------------------------------------------------- */
	/*      Prepare header block for .shp file.                             */
	/* -------------------------------------------------------------------- */
	for( i = 0; i < 100; i++ )
		abyHeader[i] = 0;

	abyHeader[2] = 0x27;				/* magic cookie */
	abyHeader[3] = 0x0a;

	i32 = 50;						/* file size */
	ByteCopy( &i32, abyHeader+24, 4 );
	if( !bBigEndian ) SwapWord( 4, abyHeader+24 );

	i32 = 1000;						/* version */
	ByteCopy( &i32, abyHeader+28, 4 );
	if( bBigEndian ) SwapWord( 4, abyHeader+28 );

	i32 = nShapeType;					/* shape type */
	ByteCopy( &i32, abyHeader+32, 4 );
	if( bBigEndian ) SwapWord( 4, abyHeader+32 );

	dValue = 0.0;					/* set bounds */
	ByteCopy( &dValue, abyHeader+36, 8 );
	ByteCopy( &dValue, abyHeader+44, 8 );
	ByteCopy( &dValue, abyHeader+52, 8 );
	ByteCopy( &dValue, abyHeader+60, 8 );

	/* -------------------------------------------------------------------- */
	/*      Write .shp file header.                                         */
	/* -------------------------------------------------------------------- */
	fwrite( abyHeader, 100, 1, fpSHP );

	/* -------------------------------------------------------------------- */
	/*      Prepare, and write .shx file header.                            */
	/* -------------------------------------------------------------------- */
	i32 = 50;						/* file size */
	ByteCopy( &i32, abyHeader+24, 4 );
	if( !bBigEndian ) SwapWord( 4, abyHeader+24 );

	fwrite( abyHeader, 100, 1, fpSHX );

	/* -------------------------------------------------------------------- */
	/*      Close the files, and then open them as regular existing files.  */
	/* -------------------------------------------------------------------- */
	fclose( fpSHP );
	fclose( fpSHX );

	return( SHPOpen( pszLayer, "r+b" ) );
}

/************************************************************************/
/*                           _SHPSetBounds()                            */
/*                                                                      */
/*      Compute a bounds rectangle for a shape, and set it into the     */
/*      indicated location in the record.                               */
/************************************************************************/

static void	_SHPSetBounds( uchar * pabyRec, SHPObject * psShape )
{
	ByteCopy( &(psShape->dfXMin), pabyRec +  0, 8 );
	ByteCopy( &(psShape->dfYMin), pabyRec +  8, 8 );
	ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 );
	ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 );

	if( bBigEndian )
	{
		SwapWord( 8, pabyRec + 0 );
		SwapWord( 8, pabyRec + 8 );
		SwapWord( 8, pabyRec + 16 );
		SwapWord( 8, pabyRec + 24 );
	}
}

/************************************************************************/
/*                         SHPComputeExtents()                          */
/*                                                                      */
/*      Recompute the extents of a shape.  Automatically done by        */
/*      SHPCreateObject().                                              */
/************************************************************************/

void SHPComputeExtents( SHPObject * psObject )
{
	int i;

	/* -------------------------------------------------------------------- */
	/*      Build extents for this object.                                  */
	/* -------------------------------------------------------------------- */
	if( psObject->nVertices > 0 )
	{
		psObject->dfXMin = psObject->dfXMax = psObject->padfX[0];
		psObject->dfYMin = psObject->dfYMax = psObject->padfY[0];
		psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0];
		psObject->dfMMin = psObject->dfMMax = psObject->padfM[0];
	}

	for( i = 0; i < psObject->nVertices; i++ )
	{
		psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]);
		psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]);
		psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]);
		psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]);

		psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]);
		psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]);
		psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]);
		psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]);
	}
}

/************************************************************************/
/*                          SHPCreateObject()                           */
/*                                                                      */
/*      Create a shape object.  It should be freed with                 */
/*      SHPDestroyObject().                                             */
/************************************************************************/

SHPObject * SHPCreateObject( int nSHPType, int nShapeId, int nParts,int * panPartStart, int * panPartType,
		int nVertices, double * padfX, double * padfY, double * padfZ, double * padfM )
{
	SHPObject *psObject;
	int i, bHasM, bHasZ;

	psObject = (SHPObject *) calloc(1,sizeof(SHPObject));
	psObject->nSHPType = nSHPType;
	psObject->nShapeId = nShapeId;

	/* -------------------------------------------------------------------- */
	/*	Establish whether this shape type has M, and Z values.		*/
	/* -------------------------------------------------------------------- */
	if( nSHPType == SHPT_ARCM
			|| nSHPType == SHPT_POINTM
			|| nSHPType == SHPT_POLYGONM
			|| nSHPType == SHPT_MULTIPOINTM )
	{
		bHasM = TRUE;
		bHasZ = FALSE;
	}
	else if( nSHPType == SHPT_ARCZ
			|| nSHPType == SHPT_POINTZ
			|| nSHPType == SHPT_POLYGONZ
			|| nSHPType == SHPT_MULTIPOINTZ
			|| nSHPType == SHPT_MULTIPATCH )
	{
		bHasM = TRUE;
		bHasZ = TRUE;
	}
	else
	{
		bHasM = FALSE;
		bHasZ = FALSE;
	}

	/* -------------------------------------------------------------------- */
	/*      Capture parts.  Note that part type is optional, and            */
	/*      defaults to ring.                                               */
	/* -------------------------------------------------------------------- */
	if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON
			|| nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM
			|| nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ
			|| nSHPType == SHPT_MULTIPATCH )
	{
		psObject->nParts = MAX(1,nParts);

		psObject->panPartStart = (int *)
			malloc(sizeof(int) * psObject->nParts);
		psObject->panPartType = (int *)
			malloc(sizeof(int) * psObject->nParts);

		psObject->panPartStart[0] = 0;
		psObject->panPartType[0] = SHPP_RING;

		for( i = 0; i < nParts; i++ )
		{
			psObject->panPartStart[i] = panPartStart[i];
			if( panPartType != NULL )
				psObject->panPartType[i] = panPartType[i];
			else
				psObject->panPartType[i] = SHPP_RING;
		}
	}

	/* -------------------------------------------------------------------- */
	/*      Capture vertices.  Note that Z and M are optional, but X and    */
	/*      Y are not.                                                      */
	/* -------------------------------------------------------------------- */
	if( nVertices > 0 )
	{
		psObject->padfX = (double *) calloc(sizeof(double),nVertices);
		psObject->padfY = (double *) calloc(sizeof(double),nVertices);
		psObject->padfZ = (double *) calloc(sizeof(double),nVertices);
		psObject->padfM = (double *) calloc(sizeof(double),nVertices);

		assert( padfX != NULL );
		assert( padfY != NULL );

		for( i = 0; i < nVertices; i++ )
		{
			psObject->padfX[i] = padfX[i];
			psObject->padfY[i] = padfY[i];
			if( padfZ != NULL && bHasZ )
				psObject->padfZ[i] = padfZ[i];
			if( padfM != NULL && bHasM )
				psObject->padfM[i] = padfM[i];
		}
	}

	/* -------------------------------------------------------------------- */
	/*      Compute the extents.                                            */
	/* -------------------------------------------------------------------- */
	psObject->nVertices = nVertices;
	SHPComputeExtents( psObject );

	return( psObject );
}

/************************************************************************/
/*                       SHPCreateSimpleObject()                        */
/*                                                                      */
/*      Create a simple (common) shape object.  Destroy with            */
/*      SHPDestroyObject().                                             */
/************************************************************************/

SHPObject * SHPCreateSimpleObject( int nSHPType, int nVertices,	double * padfX, double * padfY,	double * padfZ )
{
	return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL, nVertices, padfX, padfY, padfZ, NULL ) );
}

/************************************************************************/
/*                           SHPWriteObject()                           */
/*                                                                      */
/*      Write out the vertices of a new structure.  Note that it is     */
/*      only possible to write vertices at the end of the file.         */
/************************************************************************/

int SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject )
{
	int nRecordOffset, i, nRecordSize;
	uchar *pabyRec;
	int32 i32;

	psSHP->bUpdated = TRUE;

	/* -------------------------------------------------------------------- */
	/*      Ensure that shape object matches the type of the file it is     */
	/*      being written to.                                               */
	/* -------------------------------------------------------------------- */
	assert( psObject->nSHPType == psSHP->nShapeType || psObject->nSHPType == SHPT_NULL );

	/* -------------------------------------------------------------------- */
	/*      Ensure that -1 is used for appends.  Either blow an             */
	/*      assertion, or if they are disabled, set the shapeid to -1       */
	/*      for appends.                                                    */
	/* -------------------------------------------------------------------- */
	assert( nShapeId == -1 || (nShapeId >= 0 && nShapeId < psSHP->nRecords) );

	if( nShapeId != -1 && nShapeId >= psSHP->nRecords )
		nShapeId = -1;

	/* -------------------------------------------------------------------- */
	/*      Add the new entity to the in memory index.                      */
	/* -------------------------------------------------------------------- */
	if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords )
	{
		psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100);
		psSHP->panRecOffset = (int *) SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords );
		psSHP->panRecSize = (int *) SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords );
	}

	/* -------------------------------------------------------------------- */
	/*      Initialize record.                                              */
	/* -------------------------------------------------------------------- */
	pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) + psObject->nParts * 8 + 128);

	/* -------------------------------------------------------------------- */
	/*  Extract vertices for a Polygon or Arc.				*/
	/* -------------------------------------------------------------------- */
	if( psObject->nSHPType == SHPT_POLYGON
			|| psObject->nSHPType == SHPT_POLYGONZ
			|| psObject->nSHPType == SHPT_POLYGONM
			|| psObject->nSHPType == SHPT_ARC 
			|| psObject->nSHPType == SHPT_ARCZ
			|| psObject->nSHPType == SHPT_ARCM
			|| psObject->nSHPType == SHPT_MULTIPATCH )
	{
		int32 nPoints, nParts;
		int i;

		nPoints = psObject->nVertices;
		nParts = psObject->nParts;

		_SHPSetBounds( pabyRec + 12, psObject );

		if( bBigEndian ) SwapWord( 4, &nPoints );
		if( bBigEndian ) SwapWord( 4, &nParts );

		ByteCopy( &nPoints, pabyRec + 40 + 8, 4 );
		ByteCopy( &nParts, pabyRec + 36 + 8, 4 );

		nRecordSize = 52;

		/*
		 * Write part start positions.
		 */
		ByteCopy( psObject->panPartStart, pabyRec + 44 + 8,	4 * psObject->nParts );
		for( i = 0; i < psObject->nParts; i++ )
		{
			if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i );
			nRecordSize += 4;
		}

		/*
		 * Write multipatch part types if needed.
		 */
		if( psObject->nSHPType == SHPT_MULTIPATCH )
		{
			memcpy( pabyRec + nRecordSize, psObject->panPartType, 4*psObject->nParts );
			for( i = 0; i < psObject->nParts; i++ )
			{
				if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize );
				nRecordSize += 4;
			}
		}

		/*
		 * Write the (x,y) vertex values.
		 */
		for( i = 0; i < psObject->nVertices; i++ )
		{
			ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 );
			ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 );

			if( bBigEndian )
				SwapWord( 8, pabyRec + nRecordSize );

			if( bBigEndian )
				SwapWord( 8, pabyRec + nRecordSize + 8 );

			nRecordSize += 2 * 8;
		}

		/*
		 * Write the Z coordinates (if any).
		 */
		if( psObject->nSHPType == SHPT_POLYGONZ
				|| psObject->nSHPType == SHPT_ARCZ
				|| psObject->nSHPType == SHPT_MULTIPATCH )
		{
			ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
			if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
			nRecordSize += 8;

			ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
			if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
			nRecordSize += 8;

			for( i = 0; i < psObject->nVertices; i++ )
			{
				ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
				if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
				nRecordSize += 8;
			}
		}

		/*
		 * Write the M values, if any.
		 */
		if( psObject->nSHPType == SHPT_POLYGONM
				|| psObject->nSHPType == SHPT_ARCM
#ifndef DISABLE_MULTIPATCH_MEASURE            
				|| psObject->nSHPType == SHPT_MULTIPATCH
#endif            
				|| psObject->nSHPType == SHPT_POLYGONZ
				|| psObject->nSHPType == SHPT_ARCZ )
		{
			ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
			if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
			nRecordSize += 8;

			ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
			if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
			nRecordSize += 8;

			for( i = 0; i < psObject->nVertices; i++ )
			{
				ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
				if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
				nRecordSize += 8;
			}
		}
	}

	/* -------------------------------------------------------------------- */
	/*  Extract vertices for a MultiPoint.					*/
	/* -------------------------------------------------------------------- */
	else if( psObject->nSHPType == SHPT_MULTIPOINT
			|| psObject->nSHPType == SHPT_MULTIPOINTZ
			|| psObject->nSHPType == SHPT_MULTIPOINTM )
	{
		int32 nPoints;
		int i;

		nPoints = psObject->nVertices;

		_SHPSetBounds( pabyRec + 12, psObject );

		if( bBigEndian ) SwapWord( 4, &nPoints );
		ByteCopy( &nPoints, pabyRec + 44, 4 );

		for( i = 0; i < psObject->nVertices; i++ )
		{
			ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 );
			ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 );

			if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 );
			if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 );
		}

		nRecordSize = 48 + 16 * psObject->nVertices;

		if( psObject->nSHPType == SHPT_MULTIPOINTZ )
		{
			ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 );
			if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
			nRecordSize += 8;

			ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 );
			if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
			nRecordSize += 8;

			for( i = 0; i < psObject->nVertices; i++ )
			{
				ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 );
				if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
				nRecordSize += 8;
			}
		}

		if( psObject->nSHPType == SHPT_MULTIPOINTZ || psObject->nSHPType == SHPT_MULTIPOINTM )
		{
			ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 );
			if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
			nRecordSize += 8;

			ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 );
			if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
			nRecordSize += 8;

			for( i = 0; i < psObject->nVertices; i++ )
			{
				ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 );
				if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
				nRecordSize += 8;
			}
		}
	}

	/* -------------------------------------------------------------------- */
	/*      Write point.							*/
	/* -------------------------------------------------------------------- */
	else if( psObject->nSHPType == SHPT_POINT
			|| psObject->nSHPType == SHPT_POINTZ
			|| psObject->nSHPType == SHPT_POINTM )
	{
		ByteCopy( psObject->padfX, pabyRec + 12, 8 );
		ByteCopy( psObject->padfY, pabyRec + 20, 8 );

		if( bBigEndian ) SwapWord( 8, pabyRec + 12 );
		if( bBigEndian ) SwapWord( 8, pabyRec + 20 );

		nRecordSize = 28;

		if( psObject->nSHPType == SHPT_POINTZ )
		{
			ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 );
			if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
			nRecordSize += 8;
		}

		if( psObject->nSHPType == SHPT_POINTZ
				|| psObject->nSHPType == SHPT_POINTM )
		{
			ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 );
			if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize );
			nRecordSize += 8;
		}
	}

	/* -------------------------------------------------------------------- */
	/*      Not much to do for null geometries.                             */
	/* -------------------------------------------------------------------- */
	else if( psObject->nSHPType == SHPT_NULL )
	{
		nRecordSize = 12;
	}

	else
	{
		/* unknown type */
		assert( FALSE );
	}

	/* -------------------------------------------------------------------- */
	/*      Establish where we are going to put this record. If we are      */
	/*      rewriting and existing record, and it will fit, then put it     */
	/*      back where the original came from.  Otherwise write at the end. */
	/* -------------------------------------------------------------------- */
	if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 )
	{
		if( nShapeId == -1 )
			nShapeId = psSHP->nRecords++;

		psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize;
		psSHP->panRecSize[nShapeId] = nRecordSize-8;
		psSHP->nFileSize += nRecordSize;
	}
	else
	{
		nRecordOffset = psSHP->panRecOffset[nShapeId];
	}

	/* -------------------------------------------------------------------- */
	/*      Set the shape type, record number, and record size.             */
	/* -------------------------------------------------------------------- */
	i32 = nShapeId+1;					/* record # */
	if( !bBigEndian ) SwapWord( 4, &i32 );
	ByteCopy( &i32, pabyRec, 4 );

	i32 = (nRecordSize-8)/2;				/* record size */
	if( !bBigEndian ) SwapWord( 4, &i32 );
	ByteCopy( &i32, pabyRec + 4, 4 );

	i32 = psObject->nSHPType;				/* shape type */
	if( bBigEndian ) SwapWord( 4, &i32 );
	ByteCopy( &i32, pabyRec + 8, 4 );

	/* -------------------------------------------------------------------- */
	/*      Write out record.                                               */
	/* -------------------------------------------------------------------- */
	if( fseek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 || fwrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 )
	{
		printf( "Error in fseek() or fwrite().\n" );
		free( pabyRec );
		return -1;
	}

	free( pabyRec );

	/* -------------------------------------------------------------------- */
	/*	Expand file wide bounds based on this shape.			*/
	/* -------------------------------------------------------------------- */
	if( psSHP->adBoundsMin[0] == 0.0
			&& psSHP->adBoundsMax[0] == 0.0
			&& psSHP->adBoundsMin[1] == 0.0
			&& psSHP->adBoundsMax[1] == 0.0 
			&& psObject->nSHPType != SHPT_NULL )
	{
		psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0];
		psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0];
		psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0];
		psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0];
	}

	for( i = 0; i < psObject->nVertices; i++ )
	{
		psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]);
		psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]);
		psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]);
		psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]);
		psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]);
		psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]);
		psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]);
		psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]);
	}

	return( nShapeId  );
}

/************************************************************************/
/*                          SHPReadObject()                             */
/*                                                                      */
/*      Read the vertices, parts, and other non-attribute information	*/
/*	for one shape.							*/
/************************************************************************/

SHPObject * SHPReadObject( SHPHandle psSHP, int hEntity )
{
	SHPObject *psShape;

	/* -------------------------------------------------------------------- */
	/*      Validate the record/entity number.                              */
	/* -------------------------------------------------------------------- */
	if( hEntity < 0 || hEntity >= psSHP->nRecords )
		return( NULL );

	/* -------------------------------------------------------------------- */
	/*      Ensure our record buffer is large enough.                       */
	/* -------------------------------------------------------------------- */
	if( psSHP->panRecSize[hEntity]+8 > psSHP->nBufSize )
	{
		psSHP->nBufSize = psSHP->panRecSize[hEntity]+8;
		psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,psSHP->nBufSize);
	}

	/* -------------------------------------------------------------------- */
	/*      Read the record.                                                */
	/* -------------------------------------------------------------------- */
	fseek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 );
	fread( psSHP->pabyRec, psSHP->panRecSize[hEntity]+8, 1, psSHP->fpSHP );

	/* -------------------------------------------------------------------- */
	/*	Allocate and minimally initialize the object.			*/
	/* -------------------------------------------------------------------- */
	psShape = (SHPObject *) calloc(1,sizeof(SHPObject));
	psShape->nShapeId = hEntity;

	memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 );
	if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) );

	/* ==================================================================== */
	/*  Extract vertices for a Polygon or Arc.				*/
	/* ==================================================================== */
	if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC
			|| psShape->nSHPType == SHPT_POLYGONZ
			|| psShape->nSHPType == SHPT_POLYGONM
			|| psShape->nSHPType == SHPT_ARCZ
			|| psShape->nSHPType == SHPT_ARCM
			|| psShape->nSHPType == SHPT_MULTIPATCH )
	{
		int32 nPoints, nParts;
		int i, nOffset;

		/* -------------------------------------------------------------------- */
		/*	Get the X/Y bounds.						*/
		/* -------------------------------------------------------------------- */
		memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 +  4, 8 );
		memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
		memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
		memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );

		if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
		if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
		if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
		if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );

		/* -------------------------------------------------------------------- */
		/*      Extract part/point count, and build vertex and part arrays      */
		/*      to proper size.                                                 */
		/* -------------------------------------------------------------------- */
		memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 );
		memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 );

		if( bBigEndian ) SwapWord( 4, &nPoints );
		if( bBigEndian ) SwapWord( 4, &nParts );

		psShape->nVertices = nPoints;
		psShape->padfX = (double *) calloc(nPoints,sizeof(double));
		psShape->padfY = (double *) calloc(nPoints,sizeof(double));
		psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
		psShape->padfM = (double *) calloc(nPoints,sizeof(double));

		psShape->nParts = nParts;
		psShape->panPartStart = (int *) calloc(nParts,sizeof(int));
		psShape->panPartType = (int *) calloc(nParts,sizeof(int));

		for( i = 0; i < nParts; i++ )
			psShape->panPartType[i] = SHPP_RING;

		/* -------------------------------------------------------------------- */
		/*      Copy out the part array from the record.                        */
		/* -------------------------------------------------------------------- */
		memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts );
		for( i = 0; i < nParts; i++ )
			if( bBigEndian ) 
				SwapWord( 4, psShape->panPartStart+i );

		nOffset = 44 + 8 + 4*nParts;

		/* -------------------------------------------------------------------- */
		/*      If this is a multipatch, we will also have parts types.         */
		/* -------------------------------------------------------------------- */
		if( psShape->nSHPType == SHPT_MULTIPATCH )
		{
			memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts );
			for( i = 0; i < nParts; i++ )
				if( bBigEndian ) 
					SwapWord( 4, psShape->panPartType+i );

			nOffset += 4*nParts;
		}

		/* -------------------------------------------------------------------- */
		/*      Copy out the vertices from the record.                          */
		/* -------------------------------------------------------------------- */
		for( i = 0; i < nPoints; i++ )
		{
			memcpy(psShape->padfX + i, psSHP->pabyRec + nOffset + i * 16, 8 );

			memcpy(psShape->padfY + i, psSHP->pabyRec + nOffset + i * 16 + 8, 8 );

			if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
			if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
		}

		nOffset += 16*nPoints;

		/* -------------------------------------------------------------------- */
		/*      If we have a Z coordinate, collect that now.                    */
		/* -------------------------------------------------------------------- */
		if( psShape->nSHPType == SHPT_POLYGONZ
				|| psShape->nSHPType == SHPT_ARCZ
				|| psShape->nSHPType == SHPT_MULTIPATCH )
		{
			memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
			memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );

			if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
			if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );

			for( i = 0; i < nPoints; i++ )
			{
				memcpy( psShape->padfZ + i,	psSHP->pabyRec + nOffset + 16 + i*8, 8 );
				if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
			}

			nOffset += 16 + 8*nPoints;
		}

		/* -------------------------------------------------------------------- */
		/*      If we have a M measure value, then read it now.  We assume      */
		/*      that the measure can be present for any shape if the size is    */
		/*      big enough, but really it will only occur for the Z shapes      */
		/*      (options), and the M shapes.                                    */
		/* -------------------------------------------------------------------- */
		if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints )
		{
			memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
			memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );

			if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
			if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );

			for( i = 0; i < nPoints; i++ )
			{
				memcpy( psShape->padfM + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 );
				if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
			}
		}

	}

	/* ==================================================================== */
	/*  Extract vertices for a MultiPoint.					*/
	/* ==================================================================== */
	else if( psShape->nSHPType == SHPT_MULTIPOINT
			|| psShape->nSHPType == SHPT_MULTIPOINTM
			|| psShape->nSHPType == SHPT_MULTIPOINTZ )
	{
		int32 nPoints;
		int i, nOffset;

		memcpy( &nPoints, psSHP->pabyRec + 44, 4 );
		if( bBigEndian ) SwapWord( 4, &nPoints );

		psShape->nVertices = nPoints;
		psShape->padfX = (double *) calloc(nPoints,sizeof(double));
		psShape->padfY = (double *) calloc(nPoints,sizeof(double));
		psShape->padfZ = (double *) calloc(nPoints,sizeof(double));
		psShape->padfM = (double *) calloc(nPoints,sizeof(double));

		for( i = 0; i < nPoints; i++ )
		{
			memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 );
			memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 );

			if( bBigEndian ) SwapWord( 8, psShape->padfX + i );
			if( bBigEndian ) SwapWord( 8, psShape->padfY + i );
		}

		nOffset = 48 + 16*nPoints;

		/* -------------------------------------------------------------------- */
		/*	Get the X/Y bounds.						*/
		/* -------------------------------------------------------------------- */
		memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 +  4, 8 );
		memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 );
		memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 );
		memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 );

		if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) );
		if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) );
		if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) );
		if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) );

		/* -------------------------------------------------------------------- */
		/*      If we have a Z coordinate, collect that now.                    */
		/* -------------------------------------------------------------------- */
		if( psShape->nSHPType == SHPT_MULTIPOINTZ )
		{
			memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 );
			memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 );

			if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) );
			if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) );

			for( i = 0; i < nPoints; i++ )
			{
				memcpy( psShape->padfZ + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 );
				if( bBigEndian ) SwapWord( 8, psShape->padfZ + i );
			}

			nOffset += 16 + 8*nPoints;
		}

		/* -------------------------------------------------------------------- */
		/*      If we have a M measure value, then read it now.  We assume      */
		/*      that the measure can be present for any shape if the size is    */
		/*      big enough, but really it will only occur for the Z shapes      */
		/*      (options), and the M shapes.                                    */
		/* -------------------------------------------------------------------- */
		if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints )
		{
			memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 );
			memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 );

			if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) );
			if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) );

			for( i = 0; i < nPoints; i++ )
			{
				memcpy( psShape->padfM + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 );
				if( bBigEndian ) SwapWord( 8, psShape->padfM + i );
			}
		}
	}

	/* ==================================================================== */
	/*      Extract vertices for a point.                                   */
	/* ==================================================================== */
	else if( psShape->nSHPType == SHPT_POINT
			|| psShape->nSHPType == SHPT_POINTM
			|| psShape->nSHPType == SHPT_POINTZ )
	{
		int	nOffset;

		psShape->nVertices = 1;
		psShape->padfX = (double *) calloc(1,sizeof(double));
		psShape->padfY = (double *) calloc(1,sizeof(double));
		psShape->padfZ = (double *) calloc(1,sizeof(double));
		psShape->padfM = (double *) calloc(1,sizeof(double));

		memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 );
		memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 );

		if( bBigEndian ) SwapWord( 8, psShape->padfX );
		if( bBigEndian ) SwapWord( 8, psShape->padfY );

		nOffset = 20 + 8;

		/* -------------------------------------------------------------------- */
		/*      If we have a Z coordinate, collect that now.                    */
		/* -------------------------------------------------------------------- */
		if( psShape->nSHPType == SHPT_POINTZ )
		{
			memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 );

			if( bBigEndian ) SwapWord( 8, psShape->padfZ );

			nOffset += 8;
		}

		/* -------------------------------------------------------------------- */
		/*      If we have a M measure value, then read it now.  We assume      */
		/*      that the measure can be present for any shape if the size is    */
		/*      big enough, but really it will only occur for the Z shapes      */
		/*      (options), and the M shapes.                                    */
		/* -------------------------------------------------------------------- */
		if( psSHP->panRecSize[hEntity]+8 >= nOffset + 8 )
		{
			memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 );

			if( bBigEndian ) SwapWord( 8, psShape->padfM );
		}

		/* -------------------------------------------------------------------- */
		/*      Since no extents are supplied in the record, we will apply      */
		/*      them from the single vertex.                                    */
		/* -------------------------------------------------------------------- */
		psShape->dfXMin = psShape->dfXMax = psShape->padfX[0];
		psShape->dfYMin = psShape->dfYMax = psShape->padfY[0];
		psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0];
		psShape->dfMMin = psShape->dfMMax = psShape->padfM[0];
	}

	return( psShape );
}

/************************************************************************/
/*                            SHPTypeName()                             */
/************************************************************************/

const char * SHPTypeName( int nSHPType )
{
	switch( nSHPType )
	{
		case SHPT_NULL:
			return "NullShape";

		case SHPT_POINT:
			return "Point";

		case SHPT_ARC:
			return "Arc";

		case SHPT_POLYGON:
			return "Polygon";

		case SHPT_MULTIPOINT:
			return "MultiPoint";

		case SHPT_POINTZ:
			return "PointZ";

		case SHPT_ARCZ:
			return "ArcZ";

		case SHPT_POLYGONZ:
			return "PolygonZ";

		case SHPT_MULTIPOINTZ:
			return "MultiPointZ";

		case SHPT_POINTM:
			return "PointM";

		case SHPT_ARCM:
			return "ArcM";

		case SHPT_POLYGONM:
			return "PolygonM";

		case SHPT_MULTIPOINTM:
			return "MultiPointM";

		case SHPT_MULTIPATCH:
			return "MultiPatch";

		default:
			return "UnknownShapeType";
	}
}

/************************************************************************/
/*                          SHPPartTypeName()                           */
/************************************************************************/

const char * SHPPartTypeName( int nPartType )
{
	switch( nPartType )
	{
		case SHPP_TRISTRIP:
			return "TriangleStrip";

		case SHPP_TRIFAN:
			return "TriangleFan";

		case SHPP_OUTERRING:
			return "OuterRing";

		case SHPP_INNERRING:
			return "InnerRing";

		case SHPP_FIRSTRING:
			return "FirstRing";

		case SHPP_RING:
			return "Ring";

		default:
			return "UnknownPartType";
	}
}

/************************************************************************/
/*                          SHPDestroyObject()                          */
/************************************************************************/

void SHPDestroyObject( SHPObject * psShape )
{
	if( psShape == NULL )
		return;

	if( psShape->padfX != NULL )
		free( psShape->padfX );
	if( psShape->padfY != NULL )
		free( psShape->padfY );
	if( psShape->padfZ != NULL )
		free( psShape->padfZ );
	if( psShape->padfM != NULL )
		free( psShape->padfM );

	if( psShape->panPartStart != NULL )
		free( psShape->panPartStart );
	if( psShape->panPartType != NULL )
		free( psShape->panPartType );

	free( psShape );
}

/************************************************************************/
/*                          SHPRewindObject()                           */
/*                                                                      */
/*      Reset the winding of polygon objects to adhere to the           */
/*      specification.                                                  */
/************************************************************************/

int SHPRewindObject( SHPHandle hSHP, SHPObject * psObject )
{
	int  iOpRing, bAltered = 0;
	bAltered = (int)hSHP; // TO AVOID UNUSED PARAMETER WARNING <<< (Jon Valdes)
	bAltered = 0;
	/* -------------------------------------------------------------------- */
	/*      Do nothing if this is not a polygon object.                     */
	/* -------------------------------------------------------------------- */
	if( psObject->nSHPType != SHPT_POLYGON
			&& psObject->nSHPType != SHPT_POLYGONZ
			&& psObject->nSHPType != SHPT_POLYGONM )
		return 0;

	/* -------------------------------------------------------------------- */
	/*      Process each of the rings.                                      */
	/* -------------------------------------------------------------------- */
	for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ )
	{
		int      bInner, iVert, nVertCount, nVertStart, iCheckRing;
		double   dfSum, dfTestX, dfTestY;

		/* -------------------------------------------------------------------- */
		/*      Determine if this ring is an inner ring or an outer ring        */
		/*      relative to all the other rings.  For now we assume the         */
		/*      first ring is outer and all others are inner, but eventually    */
		/*      we need to fix this to handle multiple island polygons and      */
		/*      unordered sets of rings.                                        */
		/* -------------------------------------------------------------------- */
		dfTestX = psObject->padfX[psObject->panPartStart[iOpRing]];
		dfTestY = psObject->padfY[psObject->panPartStart[iOpRing]];

		bInner = FALSE;
		for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ )
		{
			int iEdge;

			if( iCheckRing == iOpRing )
				continue;

			nVertStart = psObject->panPartStart[iCheckRing];

			if( iCheckRing == psObject->nParts-1 )
				nVertCount = psObject->nVertices - psObject->panPartStart[iCheckRing];
			else
				nVertCount = psObject->panPartStart[iCheckRing+1] - psObject->panPartStart[iCheckRing];

			for( iEdge = 0; iEdge < nVertCount; iEdge++ )
			{
				int iNext;

				if( iEdge < nVertCount-1 )
					iNext = iEdge+1;
				else
					iNext = 0;

				if( (psObject->padfY[iEdge+nVertStart] < dfTestY && psObject->padfY[iNext+nVertStart] >= dfTestY)
						|| (psObject->padfY[iNext+nVertStart] < dfTestY && psObject->padfY[iEdge+nVertStart] >= dfTestY) )
				{
					if( psObject->padfX[iEdge+nVertStart] 
							+ (dfTestY - psObject->padfY[iEdge+nVertStart])	/ (psObject->padfY[iNext+nVertStart]
								- psObject->padfY[iEdge+nVertStart]) * (psObject->padfX[iNext+nVertStart]
								- psObject->padfX[iEdge+nVertStart]) < dfTestX )
						bInner = !bInner;
				}
			}
		}

		/* -------------------------------------------------------------------- */
		/*      Determine the current order of this ring so we will know if     */
		/*      it has to be reversed.                                          */
		/* -------------------------------------------------------------------- */
		nVertStart = psObject->panPartStart[iOpRing];

		if( iOpRing == psObject->nParts-1 )
			nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing];
		else
			nVertCount = psObject->panPartStart[iOpRing+1] - psObject->panPartStart[iOpRing];

		dfSum = 0.0;
		for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ )
		{
			dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1] - psObject->padfY[iVert] * psObject->padfX[iVert+1];
		}

		dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart] - psObject->padfY[iVert] * psObject->padfX[nVertStart];

		/* -------------------------------------------------------------------- */
		/*      Reverse if necessary.                                           */
		/* -------------------------------------------------------------------- */
		if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) )
		{
			int i;

			bAltered++;
			for( i = 0; i < nVertCount/2; i++ )
			{
				double dfSaved;

				/* Swap X */
				dfSaved = psObject->padfX[nVertStart+i];
				psObject->padfX[nVertStart+i] = psObject->padfX[nVertStart+nVertCount-i-1];
				psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved;

				/* Swap Y */
				dfSaved = psObject->padfY[nVertStart+i];
				psObject->padfY[nVertStart+i] = psObject->padfY[nVertStart+nVertCount-i-1];
				psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved;

				/* Swap Z */
				if( psObject->padfZ )
				{
					dfSaved = psObject->padfZ[nVertStart+i];
					psObject->padfZ[nVertStart+i] = psObject->padfZ[nVertStart+nVertCount-i-1];
					psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved;
				}

				/* Swap M */
				if( psObject->padfM )
				{
					dfSaved = psObject->padfM[nVertStart+i];
					psObject->padfM[nVertStart+i] = psObject->padfM[nVertStart+nVertCount-i-1];
					psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved;
				}
			}
		}
	}

	return bAltered;
}
